package com.duoqin.syncassistant.viewmodel;

import android.app.Application;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.Toast;

import com.duoqin.common.data.DefaultResponse;
import com.duoqin.common.data.remote.ApiClient;
import com.duoqin.syncassistant.R;
import com.duoqin.syncassistant.sms.SmsInfoUtil;
import com.duoqin.syncassistant.Utils;
import com.duoqin.syncassistant.data.BackupFileDownloadResponse;
import com.duoqin.syncassistant.data.BackupFileRestoreResponse;
import com.duoqin.syncassistant.data.remote.ApiInterface;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class SMSSyncViewModel extends BaseViewModel {
    private static final String TAG = "duoqin_SMSSync";

    private ApiInterface mApiInterface;
    private MutableLiveData<DefaultResponse> mSMSBackupResponse;
    private MutableLiveData<BackupFileRestoreResponse> mSMSRestoreResponse;
    private MutableLiveData<DefaultResponse> mSMSDeleteResponse;
    private MutableLiveData<BackupFileDownloadResponse> mDownloadSMSResponse;

    public SMSSyncViewModel(@NonNull Application application) {
        super(application);
        mApiInterface = ApiClient.getClient().create(ApiInterface.class);
    }

    public LiveData<DefaultResponse> smsBackupResponse() {
        if (mSMSBackupResponse == null) {
            mSMSBackupResponse = new MutableLiveData<>();
        }
        return mSMSBackupResponse;
    }

    public void backupSMS(String token, int count, String smsPath) {
        Log.d(TAG, "backupSMS");
        if (smsPath == null) return;
        byte[] smsBytes = null;
        if (smsPath != null) {
            try {
                FileInputStream fis = new FileInputStream(smsPath);
                smsBytes = new byte[fis.available()];
                fis.read(smsBytes);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (smsBytes == null) return;

        String hash = Utils.md5(smsBytes);
        Log.d(TAG, "hash = " + hash);
        RequestBody smsBody = RequestBody.create(MediaType.parse("application/octet-stream"),
                smsBytes);
        final Call<DefaultResponse> call = mApiInterface.smsBackup(token, hash, count, smsBody);
        if (call != null) {
            call.enqueue(new Callback<DefaultResponse>() {
                @Override
                public void onResponse(Call<DefaultResponse> call,
                                       Response<DefaultResponse> response) {
                    Log.d(TAG, "onResponse = " + response);
                    if (response != null && mSMSBackupResponse != null) {
                        Log.d(TAG, "body " + response.body());
                        if (200 == response.code()) {
                            mSMSBackupResponse.setValue(response.body());
                        } else {
                            try {
                                String errorString = response.errorBody().string();
                                Log.w(TAG, "error " + errorString);
                                JSONObject jsonObject = new JSONObject(errorString);
                                DefaultResponse errorResponse = new DefaultResponse();
                                errorResponse.setCode(jsonObject.getInt("code"));
                                errorResponse.setMsg(jsonObject.getString("msg"));
                                mSMSBackupResponse.setValue(errorResponse);
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                @Override
                public void onFailure(Call<DefaultResponse> call, Throwable t) {
                    Log.e(TAG, t.toString());
                    Toast.makeText(getApplication(), R.string.network_error_default,
                            Toast.LENGTH_SHORT).show();
                    if (mSMSBackupResponse != null) {
                        mSMSBackupResponse.setValue(null);
                    }
                }
            });
        }
    }

    public LiveData<BackupFileRestoreResponse> smsRestoreResponse() {
        if (mSMSRestoreResponse == null) {
            mSMSRestoreResponse = new MutableLiveData<>();
        }
        return mSMSRestoreResponse;
    }

    public void getSMSRestoreInfo(String token, int size, int page) {
        Log.d(TAG, "getSMSRestoreInfo");
        final Call<BackupFileRestoreResponse> call = mApiInterface.smsRestore(token, size, page);
        if (call != null) {
            call.enqueue(new Callback<BackupFileRestoreResponse>() {
                @Override
                public void onResponse(Call<BackupFileRestoreResponse> call,
                                       Response<BackupFileRestoreResponse> response) {
                    Log.d(TAG, "onResponse = " + response);
                    if (response != null && mSMSRestoreResponse != null) {
                        Log.d(TAG, "body " + response.body());
                        if (200 == response.code()) {
                            mSMSRestoreResponse.setValue(response.body());
                        } else {
                            try {
                                String errorString = response.errorBody().string();
                                Log.w(TAG, "error " + errorString);
                                JSONObject jsonObject = new JSONObject(errorString);
                                BackupFileRestoreResponse errorResponse = new BackupFileRestoreResponse();
                                errorResponse.setCode(jsonObject.getInt("code"));
                                errorResponse.setMsg(jsonObject.getString("msg"));
                                mSMSRestoreResponse.setValue(errorResponse);
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                @Override
                public void onFailure(Call<BackupFileRestoreResponse> call, Throwable t) {
                    Log.e(TAG, t.toString());
                    Toast.makeText(getApplication(), R.string.network_error_default,
                            Toast.LENGTH_SHORT).show();
                    if (mSMSRestoreResponse != null) {
                        mSMSRestoreResponse.setValue(null);
                    }
                }
            });
        }
    }

    public LiveData<DefaultResponse> deleteSMSResponse() {
        if (mSMSDeleteResponse == null) {
            mSMSDeleteResponse = new MutableLiveData<>();
        }
        return mSMSDeleteResponse;
    }

    public void deleteSMS(String token, String id) {
        Log.d(TAG, "deleteSMS");
        final Call<DefaultResponse> call = mApiInterface.deleteSMS(token, id);
        if (call != null) {
            call.enqueue(new Callback<DefaultResponse>() {
                @Override
                public void onResponse(Call<DefaultResponse> call,
                                       Response<DefaultResponse> response) {
                    Log.d(TAG, "onResponse = " + response);
                    if (response != null && mSMSDeleteResponse != null) {
                        if (200 == response.code()) {
                            mSMSDeleteResponse.setValue(response.body());
                            Log.d(TAG, "body " + response.body());
                        } else {
                            try {
                                String errorString = response.errorBody().string();
                                Log.w(TAG, "error " + errorString);
                                JSONObject jsonObject = new JSONObject(errorString);
                                DefaultResponse errorResponse = new DefaultResponse();
                                errorResponse.setCode(jsonObject.getInt("code"));
                                errorResponse.setMsg(jsonObject.getString("msg"));
                                mSMSDeleteResponse.setValue(errorResponse);
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                @Override
                public void onFailure(Call<DefaultResponse> call, Throwable t) {
                    Log.e(TAG, t.toString());
                    Toast.makeText(getApplication(), R.string.network_error_default,
                            Toast.LENGTH_SHORT).show();
                    if (mSMSDeleteResponse != null) {
                        mSMSDeleteResponse.setValue(null);
                    }
                }
            });
        }
    }

    public LiveData<BackupFileDownloadResponse> downloadSMSResponse() {
        if (mDownloadSMSResponse == null) {
            mDownloadSMSResponse = new MutableLiveData<>();
        }
        return mDownloadSMSResponse;
    }

    public void downloadSMS(String smsUrl, final String smsHash) {
        Log.d(TAG, "downloadSMS");
        final Call<ResponseBody> call = mApiInterface.downloadSMS(smsUrl);
        if (call != null) {
            call.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call,
                                       Response<ResponseBody> response) {
                    Log.d(TAG, "onResponse = " + response);
                    if (response != null && mDownloadSMSResponse != null) {
                        BackupFileDownloadResponse smsDownloadResponse = new BackupFileDownloadResponse();
                        if (200 == response.code()) {
                            try {
                                InputStream is = response.body().byteStream();
                                File file = new File(getApplication().getCacheDir(),
                                        SmsInfoUtil.SMS_FILE_NAME);
                                if (file.exists()) {
                                    file.delete();
                                }
                                FileOutputStream fos = new FileOutputStream(file);
                                BufferedInputStream bis = new BufferedInputStream(is);
                                byte[] buffer = new byte[1024];
                                int len;
                                MessageDigest digest = MessageDigest.getInstance("MD5");
                                while ((len = bis.read(buffer)) != -1) {
                                    fos.write(buffer, 0, len);
                                    fos.flush();
                                    digest.update(buffer, 0, len);
                                }
                                bis.close();
                                fos.close();
                                is.close();
                                Log.d(TAG, "SMS received, path = " + file.getAbsolutePath());

                                byte[] result = digest.digest();
                                String md5_computed = Utils.bytesToHex(result);
                                if (md5_computed.matches(smsHash)) {
                                    Log.d(TAG, "SMS hash is valid");
                                    smsDownloadResponse.setCode(SERVER_RESULT_SUCCESS);
                                    smsDownloadResponse.setPath(file.getAbsolutePath());
                                    mDownloadSMSResponse.setValue(smsDownloadResponse);
                                } else {
                                    Log.d(TAG, "SMS hash is invalid");
                                    file.delete();
                                    smsDownloadResponse.setCode(LOCAL_ERROR_INVALID_HASH);
                                    mDownloadSMSResponse.setValue(smsDownloadResponse);
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                smsDownloadResponse.setCode(LOCAL_ERROR_DEFAULT);
                                mDownloadSMSResponse.setValue(smsDownloadResponse);
                            }
                        } else {
                            try {
                                String errorString = response.errorBody().string();
                                Log.w(TAG, "error " + errorString);
                                JSONObject jsonObject = new JSONObject(errorString);
                                smsDownloadResponse.setCode(jsonObject.getInt("code"));
                                smsDownloadResponse.setMsg(jsonObject.getString("msg"));
                                mDownloadSMSResponse.setValue(smsDownloadResponse);
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.e(TAG, t.toString());
                    Toast.makeText(getApplication(), R.string.network_error_default,
                            Toast.LENGTH_SHORT).show();
                    if (mDownloadSMSResponse != null) {
                        mDownloadSMSResponse.setValue(null);
                    }
                }
            });
        }
    }
}
